package co.gov.medellin.ssti.sub.model.dao.impl;

import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;

import co.gov.medellin.ssti.sub.model.dao.ReporteTotalProgramasDAO;
import co.gov.medellin.ssti.sub.model.dao.exception.DAOException;
import co.gov.medellin.ssti.sub.model.vo.AreaVO;
import co.gov.medellin.ssti.sub.model.vo.ReporteTotalProgramasVO;
import co.gov.medellin.ssti.sub.web.decorator.NumeroDecoratorImpl;

public class ReporteTotalProgramasDAOImpl2 extends NamedParameterJdbcDaoSupport
		implements ReporteTotalProgramasDAO {

	private static final String SELECT_SECRETARIAS = "SELECT NOMBRE, CODIGO FROM TCS_BEN_AREA "
			+ "WHERE AREA_CODIGO IS NULL";

	public Collection<ReporteTotalProgramasVO> findReporteTotalProgramas(
			ReporteTotalProgramasVO criteria) {
		Map<String, Object> namedParameters = new HashMap<String, Object>();

		ArrayList<ReporteTotalProgramasVO> lTotalProg = new ArrayList<ReporteTotalProgramasVO>();

		StringBuffer query = new StringBuffer();

		// para la subconsulta total beneficiarios
		StringBuffer sbQueryVars = new StringBuffer();
		StringBuffer parametrosAdicionales = new StringBuffer("");
		// variables de la consulta
		// codigo de la secretaria
		if ((Object) criteria.getCodeArea() != null) {
			parametrosAdicionales.append("AND area.codigo = :area.codigo ");
			sbQueryVars.append("AND area_.codigo = :area.codigo ");
			namedParameters.put("area.codigo", criteria.getCodeArea());
		}
		// codigo del programa
		if ((Object) criteria.getCodePrograma() != null) {
			parametrosAdicionales.append("AND prog.codigo = :prog.codigo ");
			sbQueryVars.append("AND prog_.codigo = :prog.codigo ");
			namedParameters.put("prog.codigo", criteria.getCodePrograma());
		}
		// codigo del subprograma/beneficio
		if (criteria.getCodeSubprograma() != null) {
			parametrosAdicionales
					.append("AND subprog.codigo = :subprog.codigo ");
			sbQueryVars.append("AND subprog_.codigo = :subprog.codigo ");
			namedParameters
					.put("subprog.codigo", criteria.getCodeSubprograma());
		}
		// fecha inicial del reporte
		if ((Object) criteria.getDesde() != null) {
			if (criteria.getDesde().length() > 0) {
				parametrosAdicionales
						.append("AND bot.fecha_beneficio >= TO_DATE(:DESDE,'dd/mm/rrrr') ");
				sbQueryVars
						.append("AND bot_.fecha_beneficio >= TO_DATE(:DESDE,'dd/mm/rrrr') ");
				namedParameters.put("DESDE", criteria.getDesde());
			}
		}
		// fecha final del reporte
		if ((Object) criteria.getHasta() != null) {
			if (criteria.getDesde().length() > 0) {
				parametrosAdicionales
						.append("AND bot.fecha_beneficio <= TO_DATE(:HASTA,'dd/mm/rrrr') ");
				sbQueryVars
						.append("AND bot_.fecha_beneficio <= TO_DATE(:HASTA,'dd/mm/rrrr') ");
				namedParameters.put("HASTA", criteria.getHasta());
			}
		}

		// consulta del reporte:
		query.append("SELECT ");
		query.append("area.nombre NOMBRE_AREA, ");
		query.append("subarea.nombre NOMBRE_SUBAREA, ");
		query.append("prog.nombre NOMBRE_PROGRAMA, ");
		query.append("subprog.nombre NOMBRE_SUBPROGRAMA, ");
		query.append("COUNT(DISTINCT(bot.ben_codigo)) BENEFICIARIOS, ");
		query.append("COUNT(DISTINCT(bot.codigo)) BENEFICIOS, ");
		query.append("SUM(NVL(bot.valor_beneficio ,0)) VALOR ");

		// subconsulta total beneficiarios
		// subquery
		query.append(" , (SELECT COUNT(DISTINCT(ben_.codigo)) FROM tcs_ben_area subarea_ JOIN tcs_ben_subprograma subprog_ ON subarea_.codigo = subprog_.area_codigo JOIN tcs_ben_beneficio_otorgado bot_ ON bot_.sbpr_codigo = subprog_.codigo JOIN tcs_ben_beneficiario ben_ ON bot_.ben_codigo = ben_.codigo JOIN tcs_ben_programa prog_ ON subprog_.prg_codigo = prog_.codigo JOIN tcs_ben_area area_ ON area_.codigo = subarea_.area_codigo WHERE 1=1 AND prog_.vigencia = 'A' AND subprog_.vigencia = 'A' ");
		query.append(sbQueryVars.toString());
		query.append(" ) AS TOTAL_BENEFICIARIOS ");

		query.append("FROM ");
		query.append("tcs_ben_area subarea ");
		query.append("JOIN ");
		query.append("tcs_ben_subprograma subprog ");
		query.append("ON subarea.codigo = subprog.area_codigo ");
		query.append("JOIN ");
		query.append("tcs_ben_beneficio_otorgado bot ");
		query.append("ON bot.sbpr_codigo = subprog.codigo ");
		query.append("JOIN tcs_ben_beneficiario ben ");
		query.append("ON bot.ben_codigo = ben.codigo ");
		query.append("JOIN tcs_ben_programa prog ");
		query.append("ON subprog.prg_codigo = prog.codigo ");
		query.append("JOIN tcs_ben_area area ");
		query.append("ON area.codigo = subarea.area_codigo ");
		// por si no hay parametros adicionalles
		query.append("WHERE 1=1 ");
		// append parametros adicionales
		query.append(parametrosAdicionales.toString());
		query.append("AND prog.vigencia = 'A' ");
		query.append("AND subprog.vigencia = 'A' ");
		query.append("GROUP BY area.nombre,subarea.nombre,prog.nombre,subprog.nombre ");
		query.append("ORDER BY area.nombre,subarea.nombre,prog.nombre,subprog.nombre ");

		// PRECOMPILA LA SENTENCIA SQL

		ReporteTotalProgramasVO objProgramas = null;

		long subtotalBeneficiosOtorgados = 0;
		long subtotalDiferentesBeneficios = 0;
		long subtotalValor = 0;

		// nombre del programa para comparar con el siguiente registro y
		// saber si hay que agregar fila de subtotales.
		String strProg = null;

		int firstRow = 0;
		long totalBeneficiarios = 0;
		long totalProgramas = 0;
		long totalSubprogramas = 0;
		// cantidad total de beneficios otorgados.
		long totalBeneficios = 0;
		// valor total de los beneficios otorgados.
		long totalValor = 0;

		List<Map<String, Object>> rows = getNamedParameterJdbcTemplate()
				.queryForList(query.toString(), namedParameters);
		for (Map<String, Object> row : rows) {
			BigDecimal valor = (BigDecimal) row.get("VALOR");
			BigDecimal beneficios = (BigDecimal) row.get("BENEFICIOS");

			if (valor != null) {
				totalValor += valor.longValueExact();
			}
			if (beneficios != null) {
				totalBeneficios += beneficios.longValueExact();
			}
			totalSubprogramas++;

			objProgramas = new ReporteTotalProgramasVO();
			if (firstRow == 0) {
				BigDecimal totBeneficiarios = (BigDecimal) row
						.get("TOTAL_BENEFICIARIOS");
				strProg = (String) row.get("NOMBRE_PROGRAMA");
				if (totBeneficiarios != null) {
					totalBeneficiarios = totBeneficiarios.longValueExact();
				}
				firstRow++;
			}
			// si el nombre del programa de este registro no es igual al
			// anterior se debe agregar subtotales.
			if (!strProg.equals((String) row.get("NOMBRE_PROGRAMA"))) {
				// subtotalBeneficiosOtorgados = rst.getLong("BENEFICIOS");
				strProg = (String) row.get("NOMBRE_PROGRAMA");
				// agrega la fila de subtotales del programa anterior.
				objProgramas = new ReporteTotalProgramasVO();
				objProgramas.setNombreArea("SUBTOTAL:");
				objProgramas.setNombreSubprograma(Long
						.toString(subtotalDiferentesBeneficios));
				objProgramas.setCantidadBeneficios(new Long(
						subtotalBeneficiosOtorgados));
				objProgramas.setValorBeneficios(new Long(subtotalValor));
				lTotalProg.add(objProgramas);
				totalProgramas++;

				BigDecimal val = (BigDecimal) row.get("VALOR");
				if (val != null) {
					subtotalValor = val.longValueExact();
				}

				BigDecimal ben = (BigDecimal) row.get("BENEFICIOS");
				if (ben != null) {
					subtotalBeneficiosOtorgados = ben.longValueExact();
				}
				subtotalDiferentesBeneficios = 1;

				// agrega la fila del programa actual.
				objProgramas = new ReporteTotalProgramasVO();
				objProgramas.setNombreArea((String) row.get("NOMBRE_AREA"));
				objProgramas.setNombreSubarea((String) row
						.get("NOMBRE_SUBAREA"));
				objProgramas.setNombrePrograma((String) row
						.get("NOMBRE_PROGRAMA"));
				objProgramas.setNombreSubprograma((String) row
						.get("NOMBRE_SUBPROGRAMA"));
				BigDecimal bene = (BigDecimal) row.get("BENEFICIARIOS");
				if (bene != null) {
					objProgramas.setCantidadBeneficiarios(Long.toString(bene
							.longValueExact()));
				}
				if (ben != null) {
					objProgramas.setCantidadBeneficios(ben.longValueExact());
				}
				if (val != null) {
					objProgramas.setValorBeneficios(val.longValueExact());
				}
				// totalBeneficios += rst.getLong("BENEFICIOS");
				lTotalProg.add(objProgramas);

			} else {// mismo programa
				subtotalDiferentesBeneficios++;

				BigDecimal val = (BigDecimal) row.get("VALOR");
				if (val != null) {
					subtotalValor += val.longValueExact();
				}
				// totalBeneficios += rst.getLong("BENEFICIOS");

				BigDecimal ben = (BigDecimal) row.get("BENEFICIOS");
				if (ben != null) {
					subtotalBeneficiosOtorgados += ben.longValueExact();
				}
				strProg = (String) row.get("NOMBRE_PROGRAMA");

				objProgramas.setNombreArea((String) row.get("NOMBRE_AREA"));
				objProgramas.setNombreSubarea((String) row
						.get("NOMBRE_SUBAREA"));
				objProgramas.setNombrePrograma((String) row
						.get("NOMBRE_PROGRAMA"));
				objProgramas.setNombreSubprograma((String) row
						.get("NOMBRE_SUBPROGRAMA"));
				BigDecimal bene = (BigDecimal) row.get("BENEFICIARIOS");
				if (bene != null) {
					objProgramas.setCantidadBeneficiarios(Long.toString(bene
							.longValueExact()));
				}
				if (ben != null) {
					objProgramas.setCantidadBeneficios(ben.longValueExact());
				}
				if (val != null) {
					objProgramas.setValorBeneficios(val.longValueExact());
				}
				lTotalProg.add(objProgramas);
			}
		}

		if (lTotalProg != null && lTotalProg.size() > 0) {
			// ultima fila de subtotales
			objProgramas = new ReporteTotalProgramasVO();
			objProgramas.setNombreArea("SUBTOTAL:");
			objProgramas.setNombreSubprograma(Long
					.toString(subtotalDiferentesBeneficios));
			objProgramas.setCantidadBeneficios(new Long(
					subtotalBeneficiosOtorgados));
			objProgramas.setValorBeneficios(new Long(subtotalValor));
			lTotalProg.add(objProgramas);
			totalProgramas++;

			// ultimas filas de totales.
			objProgramas = new ReporteTotalProgramasVO();
			objProgramas.setNombreArea("TOTAL:");
			objProgramas.setNombreSubarea("Beneficios Otorgados:");
			objProgramas.setNombrePrograma(NumeroDecoratorImpl.dF
					.format(totalBeneficios));
			objProgramas.setNombreSubprograma("PROGRAMAS:");
			objProgramas.setCantidadBeneficiarios(NumeroDecoratorImpl.dF
					.format(totalProgramas));
			lTotalProg.add(objProgramas);

			objProgramas = new ReporteTotalProgramasVO();
			objProgramas.setNombreSubarea("Valor Beneficios Otorgados:");
			objProgramas.setNombrePrograma(NumeroDecoratorImpl.dF
					.format(totalValor));
			objProgramas.setNombreSubprograma("SUBPROGRAMAS:");
			objProgramas.setCantidadBeneficiarios(NumeroDecoratorImpl.dF
					.format(totalSubprogramas));
			lTotalProg.add(objProgramas);

			objProgramas = new ReporteTotalProgramasVO();
			objProgramas.setNombreSubarea("Beneficiarios:");
			objProgramas.setNombrePrograma(NumeroDecoratorImpl.dF
					.format(totalBeneficiarios));
			lTotalProg.add(objProgramas);
		}
		return lTotalProg;
	}

	public Collection<AreaVO> findSecretarias() {

		List<AreaVO> lareas = getNamedParameterJdbcTemplate()
				.getJdbcOperations().query(SELECT_SECRETARIAS,
						new RowMapper<AreaVO>() {

							public AreaVO mapRow(ResultSet rs, int rowNum)
									throws SQLException {
								AreaVO vo = new AreaVO();
								vo.setCode(new Long(rs.getString("CODIGO")));
								vo.setNombre(rs.getString("NOMBRE"));
								return vo;
							}
						});
		return lareas;
	}

	public ReporteTotalProgramasVO findFilters(
			ReporteTotalProgramasVO reporteTotalProgramasVO) {
		Map<String, Object> namedParameters = new HashMap<String, Object>();

		String strQuery = null;

		if (reporteTotalProgramasVO == null) {
			throw new DAOException(
					"Parametro ReporteTotalProgramasVO no puede ser null.");
		}

		if (reporteTotalProgramasVO.getCodeArea() == null
				&& reporteTotalProgramasVO.getCodePrograma() == null
				&& reporteTotalProgramasVO.getCodeSubprograma() == null) {
			return reporteTotalProgramasVO;
		}

		if (reporteTotalProgramasVO.getCodeArea() != null) {
			strQuery = "SELECT NOMBRE FROM TCS_BEN_AREA WHERE CODIGO = :CODIGO ";

			namedParameters
					.put("CODIGO", reporteTotalProgramasVO.getCodeArea());
			try {
				String nombre = getNamedParameterJdbcTemplate().queryForObject(
						strQuery, namedParameters, String.class);
				reporteTotalProgramasVO.setNombreArea(nombre);
			} catch (IncorrectResultSizeDataAccessException e) {
				// TODO: handle exception
			}
		}
		if (reporteTotalProgramasVO.getCodePrograma() != null) {
			strQuery = "SELECT NOMBRE FROM TCS_BEN_PROGRAMA WHERE CODIGO = :CODIGO ";

			namedParameters.put("CODIGO",
					reporteTotalProgramasVO.getCodePrograma());
			try {
				String nombre = getNamedParameterJdbcTemplate().queryForObject(
						strQuery, namedParameters, String.class);
				reporteTotalProgramasVO.setNombrePrograma(nombre);
			} catch (IncorrectResultSizeDataAccessException e) {
				// TODO: handle exception
			}
		}
		if (reporteTotalProgramasVO.getCodeSubprograma() != null) {
			strQuery = "SELECT NOMBRE FROM TCS_BEN_SUBPROGRAMA WHERE CODIGO = :CODIGO ";

			namedParameters.put("CODIGO",
					reporteTotalProgramasVO.getCodeSubprograma());
			try {
				String nombre = getNamedParameterJdbcTemplate().queryForObject(
						strQuery, namedParameters, String.class);
				reporteTotalProgramasVO.setNombreSubprograma(nombre);
			} catch (IncorrectResultSizeDataAccessException e) {
				// TODO: handle exception
			}
		}
		return reporteTotalProgramasVO;
	}

}
